package FlexTrade;

import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ExportException;
import java.util.Hashtable;
import java.util.LinkedList;

import net.jini.core.discovery.LookupLocator;
import net.jini.core.entry.Entry;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.UnknownEventException;
import net.jini.core.lease.Lease;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceRegistration;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryGroupManagement;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.export.Exporter;
import net.jini.jeri.BasicILFactory;
import net.jini.jeri.BasicJeriExporter;
import net.jini.jeri.tcp.TcpServerEndpoint;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.JoinManager;
import net.jini.lookup.ServiceIDListener;
import net.jini.lookup.entry.Name;
import FlexTrade.IAuction;
import FlexTrade.Auctions.DutchAuctionImpl;
import FlexTrade.Auctions.EnglishAuctionImpl;
import FlexTrade.Auctions.VickreyAuctionImpl;

/*
 * Implementation of IMarketService interface		
 */

public class MarketServiceImpl implements IMarketService, DiscoveryListener, 
								RemoteEventListener, ServiceIDListener {
	static String LUS = "jini://localhost/";
	
	private LinkedList<AuctionPrimitiveData> listOfCurrentAuctions;
	//private TransactionManager myTxnMgr = null;
	private IAuction newAuctionRequest = null;
	
	Hashtable<String, Lease> auctionLeases;
	LeaseRenewalManager leaseMgr;

	public MarketServiceImpl() {
		// TODO Auto-generated constructor stub
		lookUpLUS();
		listOfCurrentAuctions = new LinkedList<AuctionPrimitiveData>();
		auctionLeases = new Hashtable<String, Lease>();
		leaseMgr = new LeaseRenewalManager();
	}

	//a provider request a new auction
	@Override
	public void receiveNewAuctionRequestFromProvider(AuctionPrimitiveData auctionPrimitiveData, 
										RemoteEventListener provider) throws RemoteException {
		// TODO Auto-generated method stub
		//Add to list of current active auction
		listOfCurrentAuctions.add(auctionPrimitiveData);
		//create new auction object for this request
		switch(auctionPrimitiveData.auctionType) {
			case(IAuction.VICKREY_AUCTION_TYPE):
				newAuctionRequest = new VickreyAuctionImpl(auctionPrimitiveData);
				break;

			case(IAuction.ENGLISH_AUCTION_TYPE):
				newAuctionRequest = new EnglishAuctionImpl(auctionPrimitiveData);
				break;
				
			case(IAuction.DUTCH_AUCTION_TYPE):
				newAuctionRequest = new DutchAuctionImpl(auctionPrimitiveData);
				break;
				
			default:
				System.out.println("<MarketServiceImp.receiveAuctionRequest> Auction type is invalid.");
				return;
		}
		//register this object as a service to LUS
		this.registerAuctionObjectToLUS(newAuctionRequest);
		//Add provider as event listener so that provider will be updated about the status of this auction
		newAuctionRequest.addRemoteListener(provider);
		//set the time duration that the auction will be held
		newAuctionRequest.setAuctionTimeDuration(auctionPrimitiveData.timeDuration);
	}
	
	public LinkedList<AuctionPrimitiveData> getCurrentActiveAuctions() throws RemoteException{
		return this.listOfCurrentAuctions;
	}
	
	private void lookUpLUS()
	{
		try {
			//new LookupDiscoveryManager(DiscoveryGroupManagement.ALL_GROUPS, locators, this);
			new LookupDiscoveryManager(DiscoveryGroupManagement.ALL_GROUPS, null, this);
		} catch (IOException e) {
			System.err.println(e.toString());
			e.printStackTrace();
			System.exit(1);
		}
	}

	@Override
	public void discarded(DiscoveryEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void discovered(DiscoveryEvent e) {
		ServiceRegistrar[] registrars = e.getRegistrars();
		for (int n = 0; n < registrars.length; n++) 
		{
			ServiceRegistrar registrar = registrars[n];
			try {
				System.out.println("found a service locator at " + registrar.getLocator().getHost());
				//getTransactionManager(registrars[n]);
			} catch (RemoteException exp) {
				exp.printStackTrace();
				continue;
			}
		}
	}


	
	@Override
	public void serviceIDNotify(ServiceID arg0) {
		// TODO Auto-generated method stub
		System.out.println("Auction " + arg0.toString() + " is successfully placed on FlexTrade");
	}
	
	
	private void registerAuctionObjectToLUS(IAuction auctionObjec) throws RemoteException {
		Remote auctioProxy = null;
		try {
			Exporter exporter = new BasicJeriExporter(
					TcpServerEndpoint.getInstance(0),
					new BasicILFactory());
					// export an object of this class
			try {
				auctioProxy = (Remote) exporter.export((Remote) auctionObjec);
				auctionObjec.setProxy(auctioProxy);
			} catch (ExportException e) {
				e.printStackTrace();
			}			
		} catch(Exception e) {
			System.err.println(e.toString());
			e.printStackTrace();
			System.exit(1);
		}


		LookupLocator lookup = null;
		ServiceRegistrar registrar = null;
		try {
			lookup = new LookupLocator(MarketServiceImpl.LUS);
		} catch(java.net.MalformedURLException e) {
			System.err.println("Lookup failed: " + e.toString());
		}
		
		try {
			registrar = lookup.getRegistrar();
		} catch (java.io.IOException e) {
			System.err.println("Registrar search failed: " + e.toString());
		} catch (java.lang.ClassNotFoundException e) {
			System.err.println("Registrar search failed: " + e.toString());
		}
		//create template
		Entry[] serverAttributes = new Entry[1];
	    serverAttributes[0] = new Name (auctionObjec.getUniqueAuctionID());
		ServiceItem item = new ServiceItem(null, auctioProxy, serverAttributes);
		ServiceRegistration reg = null;
		try {
			//desired lease time
			long desiredDuration = auctionObjec.getAuctionPrimitiveData().timeDuration 
										+ IMarketService.TIME_DELAY_AFTER_AUCTION_FINISH;
			reg = registrar.register(item, desiredDuration);
			//this lease managed by lease renewal manager
			auctionLeases.put(auctionObjec.getUniqueAuctionID(), reg.getLease());
		} catch(java.rmi.RemoteException e) {
			System.err.println("Register exception: " + e.toString());
		}
		
		/*
		try {
			LookupDiscoveryManager mgr = new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
					null, // unicast locators
					null); // DiscoveryListener
			//template
			Entry[] serverAttributes = new Entry[1];
		    serverAttributes[0] = new Name (auctionObjec.getUniqueAuctionID());
			ServiceItem item = new ServiceItem(null, auctioProxy, serverAttributes);
		    ServiceRegistrar[] registrars = mgr.getRegistrars();
		    ServiceRegistration reg = null;
		    for(int i = 0; i< registrars.length; i++){
		    	ServiceRegistrar registrar = registrars[i];
		    	long desiredDuration = auctionObjec.getAuctionPrimitiveData().timeDuration 
										+ IMarketService.TIME_DELAY_AFTER_AUCTION_FINISH;
				reg = registrar.register(item, desiredDuration);
				auctionLeases.put(auctionObjec.getUniqueAuctionID(), reg.getLease());
		    }
		    
		    /*new JoinManager(proxy, // service proxy
				    serverAttributes, // attr sets
					this, // ServiceIDListener
					mgr, // DiscoveryManager
					new LeaseRenewalManager());
		} catch(Exception e) {
			e.printStackTrace();
		}
		*/
	}

	@Override
	public void notify(RemoteEvent arg0) throws UnknownEventException,
			RemoteException {
		// TODO Auto-generated method stub
		IAuction sourceAuction = (IAuction)arg0.getSource();
		int eventID = (int)arg0.getID();
		String sourceUniqueAuctionID = sourceAuction.getUniqueAuctionID();
		Lease lease;
		switch(eventID){
			//there is error with payment transaction. This auction is aborted and re-opened again
			case IAuction.CANNOT_TRANSFER_PAYMENT:
				//get the lease of object which raised this event
				lease = auctionLeases.remove(sourceUniqueAuctionID);
				//renew the lease
				long extendedDuration = sourceAuction.getAuctionPrimitiveData().timeDuration
											+ IMarketService.TIME_DELAY_AFTER_AUCTION_FINISH;
				leaseMgr.renewFor(lease, extendedDuration, null);
				//put back to hash table
				auctionLeases.put(sourceUniqueAuctionID, lease);
				break;
			//payment transfered successfully. Remove the lease from hash table	
			case IAuction.PAYMENT_TRANSFERED_SUCCESSFULLY:
				//remove lease from hash table
				auctionLeases.remove(sourceUniqueAuctionID);
				break;
			default:
		}
		
	}
}

/*===============================================================================================/
 * REMOVE CODE
/
/*
	//Get the transaction manager
	private void getTransactionManager(ServiceRegistrar registrar) {
		try{
			Class[] classes = {TransactionManager.class};
			//serverAttributes[0] = new Name ("TransactionManager");
		    //ServiceTemplate template = 
		    //		  new ServiceTemplate (null, null, serverAttributes);
		    ServiceTemplate template = 
		    		  new ServiceTemplate (null, classes, null);
		    this.myTxnMgr = (TransactionManager) registrar.lookup(template);
		    if(this.myTxnMgr == null){
		    	System.out.println("Cannot retrieve TransactionManager object.");
		    }
		} catch(RemoteException e) {
			System.out.println("Cannot retrieve TransactionManager.");
			e.printStackTrace();
		}
	}
	
		private TransactionManager findTxnMgr() {
		ServiceDiscoveryManager clientMgr = null;
		try {
			LookupDiscoveryManager mgr = new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
																	null, // unicast locators
																	null); // DiscoveryListener
			clientMgr = new ServiceDiscoveryManager(mgr, new LeaseRenewalManager());
		} catch(Exception e) {
			e.printStackTrace();
			return null;
			//System.exit(1);
		}
		Class [] classes = new Class[] {TransactionManager.class};
		ServiceTemplate template = new ServiceTemplate(null, classes,null);
		ServiceItem item = null;
		// Try to find the service, blocking until timeout if necessary
		try {
			item = clientMgr.lookup(template,
										null, // no filter
										100000); // timeout
		} catch(Exception e) {
			e.printStackTrace();
			return null;
			//System.exit(1);
		}
		if (item == null) {
			// couldn't find a service in time
			System.out.println("No transaction manager service.");
			return null;
			//System.exit(1);
		}
		// Get the service
		TransactionManager mgr = (TransactionManager) item.service;
		if (mgr == null) {
			System.out.println("Transaction Manager is null.");
			return null;
			//System.exit(1);
		}
		return mgr;
	}
*/